<?php
// kit applicatif php 0.5
// classe de transmission de variables entre evenements, et enregistrement en session etc
// file version 2005-05-26 16:48:40
// http://code.fredbird.org

class wakvarManager extends wakCoreComponent {
/*----------------------------------------------------------------------------------------------------
																														proprietes
----------------------------------------------------------------------------------------------------*/
	var $data=array(); // stockage interne des variables
	//var $stored=array(); // liste des variables de session a recuperer
	var $default_parameters=array(
		'useSession'=>false,
		'sessionName'=>'SID',
		'slashGPC'=>true
	);
	var $parameters;
	var $session_retrieved=false;
/*----------------------------------------------------------------------------------------------------
																														constructeur
----------------------------------------------------------------------------------------------------*/
	function wakvarManager (& $controller) {
		// appel du constructeur parent
		$const=get_parent_class($this);
		parent::$const($controller);
		if ($this->parameters['useSession']) $this->_getSession();
		// generation d'un identifiant unique
		//$this->id=uniqid('varmanager');
		//echo affvar($this->stored);
		//echo affvar($this->data);
	}
/*----------------------------------------------------------------------------------------------------
																												methodes publiques
----------------------------------------------------------------------------------------------------*/
	// si une variable n'est pas definie, lui donne sa valeur initiale
	function init($key,$value) {
		if (!isset($this->data[$key]) or $this->data[$key]==NULL)
			$this->data[$key]=$value;
		//wakLog("varManager: init ".affvar);
		//echo $this->id.'= '.affvar($this->data);
		//return 
	}

	// enregistre la variable en donnee interne
	function set($key,$value=NULL) {
		//echo $this->id.'= '.affvar($this->data);
		//define($key,strval($value));
		if (!is_string($key)) return false; // $key=strval($key);
		$this->data[$key]=$value;
		//wakLog("varManager: setting $key=".strval($value));
		//echo serialize($this);
		//echo $this->id.'= '.affvar($this->data);
		return true;
	}
	// enregistre en session, si la valeur est nulle on la recherche en memoire interne
	// la clef sera integree depuis la session dans la memoire interne par le constructeur
	function store($key,$value=NULL) {
		if ($value===NULL and isset($this->data[$key])) $value=$this->data[$key];
		//$this->data[$key]=$value;
		// stockage en session
		if (!isset($_SESSION)) return false;
		$_SESSION[$key]=& $value;
		$this->data[$key]=& $_SESSION[$key];
		return true;
	}
	// recherche la clef en memoire interne
	function get($key) {
		//$this->C->log($this->id." getting $key");
		//echo $this->id.'= '.affvar($this->data);
		if (!isset($this->data[$key])) return NULL;
		return $this->data[$key];
	}
	
	function & getArray($keys=array()) {
		$values=array();
		foreach ($keys as $key) {
			$values[$key]=$this->get($key);
		}
		return $values;
	}
	
	// recherche la clef en memoire ou dans les tableaux globaux, renvoie la premiere occurence
	// par ex search = "tmp_session_post_get_cookie"
	function getIn($key,$search="") {
		$namespaces=explode('_',strtolower($search));
		foreach ($namespaces as $space) {
			switch ($space) {
			case 'tmp':
				if (isset($this->data[$key])) return $this->data[$key];
				break;
			case 'session':
				if (isset($_SESSION[$key])) return $_SESSION[$key];
				break;
			case 'post':
			case 'get':
			case 'cookie':
				$value=$this->getGPCVar($space,$key);
				if ($value!==NULL) return $value;
				break;
				/*case ($space=='post' and isset($_POST[$key])):
					return $_POST[$key];
					break;
				case ($space=='get' and isset($_GET[$key])):
					return $_GET[$key];
					break;
				case ($space=='cookie' and isset($_COOKIE[$key])):
					return $_COOKIE[$key];
					break;*/
			}
		}
		return NULL;
	}
	function & getArrayIn($keys=array(),$search='tmp') {
		$values=array();
		foreach ($keys as $key) {
			$values[$key]=$this->getin($key,$search);
		}
		return $values;
	}
	
	function destroy($key) {
		if (isset($this->data[$key])) unset($this->data[$key]);
		if (isset($_SESSION[$key])) unset($_SESSION[$key]);
		if (isset($GLOBALS[$key])) unset($GLOBALS[$key]);
		// destruction get et post ?
		if (isset($_GET[$key])) unset($_GET[$key]);
		if (isset($_POST[$key])) unset($_POST[$key]);
		if (isset($_COOKIE[$key])) unset($_COOKIE[$key]);
		return true;
	}
	
	// recup de parametres
	// sur le modele name::type::value;;name2::type2::value2 etc
	function & retrieveParams($string=NULL) {
		//$this->C->log("retrieving params ".trim($string));
		if (!$string or $string=='NULL') return array();
		// nettoyage caracteres de controle
		//$string=trim($string, "\x00..\x1F");
		//$string=trim($string);
		$params=array(); // le tableau de parametres qui sera retourné
		$list=explode(';;',$string);
		foreach ($list as $item) {
			$param=explode('::',$item);
			if (count($param)==2) {
				// type non precisé, => value
				$param[2]=$param[1]; $param[1]='value';
			} else if (count($param)!=3) continue;
			$key=trim($param[0]);
			switch (trim($param[1])) {
				case 'value': // valeur brute de la chaine passee
					$params[$key]=trim($param[2]);
					break;
				case 'var': // recherche de la clef dans les varaibles stockées par varManager
					$params[$key]=& $this->get(trim($param[2]));
					break;
				case 'get': // recherche de la clef dans les donnees GET
					$params[$key]= $this->getIn($key,'get');
					break;
				case 'constant': // recherche dans les constantes definies
					//echo affvar(get_defined_constants(),false);
					if (!defined(trim($param[2]))) $params[$key]=NULL;
					else $params[$key]=constant(trim($param[2]));
					break;
				case 'urisegment': // recup de la valeur en tant que N-ieme segment de l'uri
					// recupere N, renumerotation a partir de zero
					$index=max(0,intval($param[2])-1); // echo $index;
					$segments=explode('/',BASE_URI);// echo affvar($segments);
					if (isset($segments[$index]))
						$params[$key]=$segments[$index];
					else $params[$key]=NULL;
					break;
				case 'uriregex': // recup de la valeur par regex sur l'uri
					$ereg=trim($param[2]);
					if (ereg($ereg,CALL_URI,$search=array())) {
						$params[$key]=$search[1];
						//echo affvar($search);
					}
					else {
						$params[$key]=NULL;
						//echo affvar($ereg)." ".CALL_URI;
					}
					break;
			}
		}
		return $params;
	}
	// retourne toutes les variables disponibles dans un tableau unique
	function & allVars() {
		$all=array();
		/*if (count($this->data)>0)*/ $all['_wak']=& $this->data;
		if (isset($_SESSION)) $all['_session']=& $_SESSION;
		if (count($_GET)>0) $all['_get']=& $_GET;
		if (count($_POST)>0) $all['_post']=& $_POST;
		if (count($_COOKIE)>0) $all['_cookie']=& $_COOKIE;
		if (count($_FILES)>0) $all['_files']=& $_FILES;
		//if (count($_SERVER)>0) $all['server']=& $_SERVER;
		//if (count($_ENV)>0) $all['env']=& $_ENV;
		return $all;
	}
	function & getAll() {
		return $this->allVars();
	}
	
	// fusionne deux tableaux recursivement, avec remplacement des collisions
	function & array_merge_recursive_replace(&$array_1,&$array_2) {
		$merged=$array_1;
		$keys_1=array_keys($array_1);
		$keys_2=array_keys($array_2);
		foreach ($merged as $key => $value) {
			if (!isset($array_2[$key])) continue;
			if (is_array($value) and is_array($array_2[$key])) {
				// appel recursif
				$merged[$key]=$this->array_merge_recursive_replace($value,$array_2[$key]);
			} else {
				$merged[$key]=$array_2[$key];
			}
		}
		foreach ($array_2 as $key => $value)
			if (!isset($merged[$key])) $merged[$key]=$value;
		return $merged;
	}
	
	// décrit une variable en xml (récursif)
	function varToXml($var, $key='',$nivMax=4,$niv=1) {
		$retour="";
		// objets trop complexes ou references menant a une boucle infinie
		if ($niv>$nivMax) return false;
		// determination et affichage du type
		switch($type=gettype($var)) {
			case "boolean":
				if ($var==true) $retour='<boolean key="'.$key.'">true</boolean>'."\n";
				else $retour='<boolean key="'.$key.'">false</boolean>';
				break;
			case "integer":
			case "float": 
			case "string":
				$retour.='<'.$type.' key="'.$key.'">'.$this->xmlentities($var).'</'.$type.">\n";
				break;
			case "resource": 
				$retour='<resource key="'.$key.'">'.get_resource_type($var).'</resource>'."\n";
				break;
			case "array": $n=0;
				$retour.='<array key="'.$key.'" size="'.count($var).'">'."\n";
				foreach($var as $varkey => $value) {
					$retour.=$this->varToXml($value,$varkey,$nivMax,$niv);
				}
				$retour.="</array>\n";
				break;
			case "object":
				$retour='<object key="'.$key.'" class="'.get_class($var).'"';
				if ($parent=get_parent_class($var)) $retour.=' extends="'.$parent.'"';
				$retour.=">\n";
				foreach( get_class_methods($var) as $m) 
					$retour.='<method name="'.$m.'" />'."\n";
				if ($members=get_object_vars($var)) {
					$retour.='<members>';
					foreach ($members as $mKey => $mValue)
						$retour.=$this->varToXml($mValue, $mKey,$nivMax,$niv);
					$retour.="</members>\n";
				}
				break;
			case "NULL": 
				$retour.='<null key="'.$key.' />'."\n";
				break;
			default: $retour.='<unkown type="'.$type.'">'."\n";
				break;
		} // fin du switch
		return $retour;
	}
	// opération inverse de la précedente
	function varFromXml($xml) {
		$tags=$this->searchXmlTags($xml);
		echo affvar($tags);
	}
	// retourne la declaration php d'une variable (recursif)
	// si key est nul, seule la partie valeur est retournée
	function varToDeclaration($var,$key=NULL) {
		switch (true) {
		case is_bool($var):
			if ($var) $r='true';
			else $r='false';
			break;
		case is_null($var):
			$r='NULL';
			break;
		case is_string($var):
			$r="'".str_replace("'","\'",$var)."'";
			break;
		//case is_int($var):
		//case is_double($var):
		//case is_float($var):
		case is_numeric($var):
		//case 'double':
		//case 'decimal':
		//case 'integer':
		//case 'float':
		//case 'real':
			$r=$var;
			break;
		case is_array($var):
			$r="array(\n";
			foreach ($var as $key2 => $value) {
				$r.="'$key2'=>".$this->varToDeclaration($value).",";
			}
			// suppression derniere virgule superflue
			if (substr($r,-1,1)==',') $r=substr($r,0,-1);
			$r.=")";
			break;
		default:
			//echo gettype($var).':'.$var;
			$r='NULL';
			break;
		}
		//echo affvar($r,'var_declaration');
		if ($key!==NULL) return $key."=".$r.";";
		else return $r;
	}
	
/*----------------------------------------------------------------------------------------------------
																												methodes privees
----------------------------------------------------------------------------------------------------*/
	function _getSession() {
		if ($this->session_retrieved) return true;
		session_name($this->settings['sessionName']);
		session_start();
		for (reset($_SESSION); $key=key($_SESSION); next($_SESSION))
			$this->data[$key]=& $_SESSION[$key];
		$this->session_retrieved=true;
	}
	
	function & xmlentities($str) {
		$str=str_replace('&','&amp;',$str);
		$str=str_replace('"','&quot;',$str);
		$str=str_replace("'",'&apos;',$str);
		$str=str_replace('<','&lt;',$str);
		$str=str_replace('>','&gt;',$str);
		return $str;
	}
	
	function getGPCVar($space,$key) {
		$space=strtolower($space);
		switch ($space) {
		case 'post':
			if (!isset($_POST[$key])) return NULL;
			$value=$_POST[$key];
			break;
		case 'get':
			if (!isset($_GET[$key])) return NULL;
			$value=$_GET[$key];
			break;
			case 'cookie':
			if (!isset($_COOKIE[$key])) return NULL;
			$value=$_COOKIE[$key];
			break;
		default:
			return NULL;
			break;
		}
		// ajout de slash si nécessaire
		if ($this->parameters['slashGPC'] and !get_magic_quotes_gpc())
			$value=addslashes($value);
		// enlevement de slashes si nécessaire
		else if (!$this->parameters['slashGPC'] and get_magic_quotes_gpc())
			$value=stripslashes($value);
		return $value;
	}
	
	// analyse rapide de xml
	function searchXmlTags($xml) {
		$name="[a-zA-Z_0-9]+"; $value='[^"]*';
		//$attr=" +($name)=\"($value)\"";
		//$ereg='</?('.$name.')('.$attr.')*/?'.'>'; echo $ereg;
		$ereg='</?('.$name.')[^>]*>';// echo $ereg;
		$n=0; $results=array();
		while (ereg($ereg,$xml,$found)) {
			$tag=&$found[0];
			$attributes=$this->tagAttributes($tag);
			$results[]=array('tag'=>$tag,'name'=>$found[1],'attributes'=>$attributes);
			$xml=ereg_replace($tag,'',$xml);
			$n++; if ($n>50) break;
		}
		return $results;
	}
	
	function tagAttributes($tag) {
		$att=array();
		$name="[a-zA-Z_0-9]+"; $value='[^"]*';
		$ereg=" +($name)=\"($value)\"";
		while (ereg($ereg,$tag,$found)) {
			$att[$found[1]]=$found[2];
			$tag=ereg_replace($found[0],'',$tag);
		}
		
		return $att;
	}
	
	/*retourne en XHTML, recursivement, le contenu et le type de var
	si cut vaut true, alors les chaines et les tableaux seront tronqués
	*/
	function affVar($var, $name=NULL, $cut=50, $niv=1) {
		$cut=intval($cut);
		$retour="";
		// objets trop complexes ou references menant a une boucle infinie
		if ($niv>7) return false;
		if ($name!==NULL) $retour.=strval($name)." = ";
		// determination et affichage du type
		switch(gettype($var)) {
			case "boolean":
				if ($var==true) $retour.= "boolean <strong>true</strong>";
				else $retour.= "<em>boolean</em> <strong>false</strong>";
				break;
	
			case "integer": 
				$retour.= "<em>integer</em> <strong>$var</strong>";
				break;
	
			case "float": 
				$retour.= "<em>float</em> <strong>$var</strong>";
				break;
	
			case "string":
				// raccourcissement de la chaine
				if (is_int($cut) and strlen($var)>($cut*10))
					 $retour.= "<em>string</em>(".strlen($var).") <strong>".htmlentities(substr($var,0,($cut*10)))."</strong>...+";
				else $retour.= "<em>string</em>(".strlen($var).") <strong>".htmlentities($var)."</strong>";
				break;
	
			case "resource": 
				$retour.= "<em>resource</em> ".get_resource_type($var);
				break;
	
			case "array": $n=0;
				$retour.="<em>array</em> (".count($var).")<br />\n";
				foreach($var as $key => $value) {
					// indentation
					for ($f=1; $f<=$niv; $f++)
						$retour.=". ";
					// cutting
					$n++; if (is_int($cut) and $n>$cut) { $retour.="[+".(count($var)-$cut)."]<br />\n"; break; }
					$retour.=$this->affvar($value,$key,$cut,$niv+1)."<br />\n";
				}
				break;
	
			case "object":
				$retour="<em>object</em> <strong>".get_class($var)."</strong>";
				if ($parent=get_parent_class($var)) $retour.=" extends ".$parent;
				$retour.="<br/>\n";
				// indentation
				for ($f=1; $f<=$niv; $f++) $retour.=". ";
				$retour.="<em>methods :</em> ";
				foreach( get_class_methods($var) as $m) $retour.=$m."(); ";
				$retour.="<br/>"		;
				// indentation
				for ($f=1; $f<=$niv; $f++) $retour.=". ";
				$retour.=$this->affvar(get_object_vars($var), 'properties', $cut, $niv+1);
				break;
			case "NULL": 
				$retour.="<strong>NULL</strong>";
				break;
	
			default: $retour.="<strong>unkown type</strong>";
				break;
		} // fin du switch
	
		return $retour;
	}
}
?>
